package net.simpleframework.swing;

import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.datatransfer.StringSelection;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.filechooser.FileFilter;
import javax.swing.plaf.metal.MetalLookAndFeel;

import net.simpleframework.util.BeanUtils;
import net.simpleframework.util.LocaleI18n;
import net.simpleframework.util.StringUtils;

import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.jdesktop.swingx.util.WindowUtils;

import com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel;

/**
 * 这是一个开源的软件，请在LGPLv3下合法使用、修改或重新发布。
 * 
 * @author 陈侃(cknet@126.com, 13910090885)
 *         http://code.google.com/p/simpleframework/
 *         http://www.simpleframework.net
 */
public abstract class SwingUtils {
	static final Font defautFont = new Font(Font.DIALOG, Font.PLAIN, 12);

	static {
		LocaleI18n.addBasename(SwingUtils.class);
		// for (final String key : new String[] { "Component.font",
		// "TextField.font", "Button.font",
		// "ToggleButton.font", "RadioButton.font", "CheckBox.font",
		// "ColorChooser.font",
		// "ComboBox.font", "Label.font", "List.font", "MenuBar.font",
		// "MenuItem.font",
		// "RadioButtonMenuItem.font", "CheckBoxMenuItem.font", "Menu.font",
		// "PopupMenu.font",
		// "OptionPane.font", "Panel.font", "ProgressBar.font",
		// "ScrollPane.font",
		// "Viewport.font", "TabbedPane.font", "Table.font", "TableHeader.font",
		// "TitledBorder.font", "ToolBar.font", "Tree.font" }) {
		// UIManager.getDefaults().put(key, defautFont);
		// }
	}

	public static void copySystemClipboard(final String text) {
		final StringSelection stringSelection = new StringSelection(text);
		Toolkit.getDefaultToolkit().getSystemClipboard()
				.setContents(stringSelection, stringSelection);
	}

	public static boolean confirm(final Window owner, final String message) {
		return JOptionPane.showConfirmDialog(owner, message, LocaleI18n.getMessage("SwingUtils.0"),
				JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
	}

	public static void showError(final Window owner, final String message, final Throwable th) {
		new ErrorDialog(owner, message, th);
	}

	public static void showError(final Window owner, final String message) {
		new ErrorDialog(owner, message);
	}

	public static void showError(final Window owner, final Throwable th) {
		new ErrorDialog(owner, th);
	}

	public static JFileChooser createJFileChooser(final String[] types, final String description) {
		final JFileChooser chooser = new JFileChooser();
		chooser.removeChoosableFileFilter(chooser.getAcceptAllFileFilter());
		chooser.addChoosableFileFilter(new FileFilter() {
			@Override
			public boolean accept(final File f) {
				if ((types == null) || (types.length == 0)) {
					return true;
				} else {
					if (!f.isFile()) {
						return true;
					} else {
						final String ext = StringUtils.getFilenameExtension(f.getPath());
						for (final String type : types) {
							if (type.equalsIgnoreCase(ext)) {
								return true;
							}
						}
						return false;
					}
				}
			}

			@Override
			public String getDescription() {
				return description;
			}
		});
		return chooser;
	}

	public static void showPopupMenu(final JPopupMenu popup, final Component component, int x, int y) {
		final Point p = new Point(x, y);
		SwingUtilities.convertPointToScreen(p, component);
		final Dimension size = popup.getPreferredSize();
		final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();

		boolean horiz = false;
		boolean vert = false;

		final int origX = x;

		if ((p.x + size.width > screen.width) && (size.width < screen.width)) {
			x += (screen.width - p.x - size.width);
			horiz = true;
		}

		if ((p.y + size.height > screen.height) && (size.height < screen.height)) {
			y += (screen.height - p.y - size.height);
			vert = true;
		}

		if (horiz && vert) {
			x = origX - size.width - 2;
		}
		popup.show(component, x, y);
	}

	public static Icon createEmptyIcon16() {
		return new Icon() {
			@Override
			public int getIconHeight() {
				return 16;
			}

			@Override
			public int getIconWidth() {
				return 16;
			}

			@Override
			public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
			}
		};
	}

	public static void centerWithinScreen(final Window wind) {
		if (wind == null) {
			return;
		}
		final Toolkit toolKit = Toolkit.getDefaultToolkit();
		final Rectangle rcScreen = new Rectangle(toolKit.getScreenSize());
		final Dimension windSize = wind.getSize();
		final Dimension parentSize = new Dimension(rcScreen.width, rcScreen.height);
		if (windSize.height > parentSize.height) {
			windSize.height = parentSize.height;
		}
		if (windSize.width > parentSize.width) {
			windSize.width = parentSize.width;
		}
		center(wind, rcScreen);
	}

	private static void center(final Component wind, final Rectangle rect) {
		if ((wind == null) || (rect == null)) {
			return;
		}
		final Dimension windSize = wind.getSize();
		final int x = ((rect.width - windSize.width) / 2) + rect.x;
		int y = ((rect.height - windSize.height) / 2) + rect.y;
		if (y < rect.y) {
			y = rect.y;
		}
		wind.setLocation(x, y);
	}

	public static Window findWindow(final Component c) {
		return WindowUtils.findWindow(c);
	}

	public static void setCursor(final Component component, final Cursor cursor) {
		component.setCursor(cursor);
		final Component r = findWindow(component);
		if (r != null) {
			r.setCursor(cursor);
		}
	}

	public static void setWaitCursor(final Component component) {
		setCursor(component, Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
	}

	public static void setDefaultCursor(final Component component) {
		setCursor(component, Cursor.getDefaultCursor());
	}

	public static void setEnabled(final Container c, final boolean enabled) {
		setEnabled(new Container[] { c }, enabled);
	}

	public static void setEnabled(final Container[] containers, final boolean enabled) {
		for (final Container c : containers) {
			for (final Component component : WindowUtils.getAllComponents(c)) {
				component.setEnabled(enabled);
			}
			c.setEnabled(enabled);
		}
	}

	public static void setJButtonSizesTheSame(final JButton[] btns) {
		if (btns == null) {
			throw new IllegalArgumentException();
		}

		final Dimension maxSize = new Dimension(0, 0);
		for (int i = 0; i < btns.length; ++i) {
			final JButton btn = btns[i];
			final FontMetrics fm = btn.getFontMetrics(btn.getFont());
			final Rectangle2D bounds = fm.getStringBounds(btn.getText(), btn.getGraphics());
			final int boundsHeight = (int) bounds.getHeight();
			final int boundsWidth = (int) bounds.getWidth();
			maxSize.width = boundsWidth > maxSize.width ? boundsWidth : maxSize.width;
			maxSize.height = boundsHeight > maxSize.height ? boundsHeight : maxSize.height;
		}

		final Insets insets = btns[0].getInsets();
		maxSize.width += insets.left + insets.right;
		maxSize.height += insets.top + insets.bottom;

		for (int i = 0; i < btns.length; ++i) {
			final JButton btn = btns[i];
			btn.setPreferredSize(maxSize);
		}

		initComponentHeight(btns);
	}

	public static Icon loadIcon(final String name) {
		final InputStream is = SwingUtils.class.getClassLoader().getResourceAsStream(
				BeanUtils.getResourceClasspath(SwingUtils.class, "images/" + name));
		try {
			return new ImageIcon(GraphicsUtilities.loadCompatibleImage(is));
		} catch (final IOException e) {
			return null;
		}
	}

	/*--------------------------ui creator--------------------------*/

	public static Border createTitleBorder(final String title) {
		final Border tb = BorderFactory.createTitledBorder(title);
		final Border eb = BorderFactory.createEmptyBorder(0, 5, 5, 5);
		return BorderFactory.createCompoundBorder(tb, eb);
	}

	public static JPanel createVerticalPanel(final Component... components) {
		return createVerticalPanel((String) null, components);
	}

	public static JPanel createVerticalPanel(final String title, final Component... components) {
		final JPanel jp = new JPanel(new GridBagLayout());
		if (components != null) {
			final GridBagConstraints gbc = new GridBagConstraints();
			gbc.gridx = 0;
			gbc.gridy = 0;
			gbc.insets = new Insets(0, 5, 4, 5);
			gbc.anchor = GridBagConstraints.NORTHWEST;
			gbc.weightx = 1.0;
			for (final Component component : components) {
				if (gbc.gridy == components.length - 1) {
					gbc.weighty = 1.0;
				}
				if (component instanceof JButton) {
					gbc.fill = GridBagConstraints.NONE;
					initComponentHeight(component);
				} else {
					gbc.fill = GridBagConstraints.HORIZONTAL;
				}
				jp.add(component, gbc);
				gbc.gridy++;
			}
		}
		if (StringUtils.hasText(title)) {
			jp.setBorder(createTitleBorder(title));
		}
		return jp;
	}

	public static JPanel createFlowPanel(final Object... components) {
		return createFlowPanel(FlowLayout.LEFT, 0, components);
	}

	public static JPanel createFlowPanel(final int align, final int gap, final Object... components) {
		final JPanel jp = new JPanel(new FlowLayout(align, gap, gap));
		jp.setBorder(BorderFactory.createEmptyBorder());
		if (components != null) {
			for (final Object component : components) {
				if (component instanceof Component) {
					initComponentHeight((Component) component);
					jp.add((Component) component);
				} else if (component instanceof Number) {
					jp.add(Box.createHorizontalStrut(((Number) component).intValue()));
				}
			}
		}
		return jp;
	}

	public static JPanel createTextButtonsPanel(final JTextField textField, final JButton... btns) {
		final JPanel jp = new JPanel(new GridBagLayout());
		jp.setBorder(textField.getBorder());
		textField.setBorder(BorderFactory.createEmptyBorder());
		initComponentHeight(textField);
		jp.setPreferredSize(textField.getPreferredSize());
		jp.setBackground(textField.getBackground());
		final GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.weightx = 1.0;
		gbc.weighty = 1.0;
		gbc.anchor = GridBagConstraints.NORTH;
		gbc.fill = GridBagConstraints.BOTH;
		jp.add(textField, gbc);
		gbc.weightx = 0.0;
		gbc.insets = new Insets(0, 1, 1, 1);
		if (btns != null) {
			final Dimension btnDim = new Dimension(18, textField.getPreferredSize().height);
			for (final JButton btn : btns) {
				gbc.gridx++;
				btn.setMaximumSize(btnDim);
				btn.setMinimumSize(btnDim);
				btn.setPreferredSize(btnDim);
				btn.setText("...");
				jp.add(btn, gbc);
			}
		}
		return jp;
	}

	public static Component createKeyValueComponents(final Component key, final Component value) {
		return createKeyValueComponents(key, value, 55);
	}

	public static Component createKeyValueComponents(final Component key, final Component value,
			final int keyWidth) {
		initComponentHeight(key, value);
		if (keyWidth > 0) {
			key.setPreferredSize(new Dimension(keyWidth, key.getPreferredSize().height));
		}
		final JPanel jp = new JPanel(new GridBagLayout());
		final GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.insets = new Insets(0, 0, 0, 4);
		jp.add(key, gbc);
		gbc.gridx = 1;
		gbc.insets = new Insets(0, 0, 0, 0);
		gbc.weightx = 1.0;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		jp.add(value, gbc);
		return jp;
	}

	public static JToolBar createToolBar() {
		return new JToolBar() {
			private static final long serialVersionUID = 6619134509055370148L;

			{
				setFloatable(false);
				setRollover(true);
				setBorderPainted(false);
				setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
			}

			final Insets margin = new Insets(2, 8, 2, 8);

			@Override
			public JButton add(final Action a) {
				final JButton btn = super.add(a);
				btn.setHideActionText(false);
				btn.setFocusPainted(false);
				btn.setMargin(margin);
				return btn;
			}
		};
	}

	static final String LAF_WINDOWS = WindowsClassicLookAndFeel.class.getName();

	static final String LAF_METAL = MetalLookAndFeel.class.getName();

	public static void initComponentHeight(final Component... components) {
		if (components != null) {
			for (final Component component : components) {
				if ((component instanceof JComboBox) || (component instanceof JButton)) {
					component.setPreferredSize(new Dimension(component.getPreferredSize().width, 22));
				} else if (component instanceof JTextField) {
					final String lf = UIManager.getLookAndFeel().getClass().getName();
					int i = 22;
					if (lf.equals(LAF_METAL)) {
						i = 23;
					}
					component.setPreferredSize(new Dimension(component.getPreferredSize().width, i));
				}
			}
		}
	}
}
